﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Graphics;
using Tesla.Math;
using Tesla.Xna.Graphics.Implementation;
using Tesla.Xna.Util;
using XFG = Microsoft.Xna.Framework.Graphics;
using XNA = Microsoft.Xna.Framework;

namespace Tesla.Xna.Graphics {
    /// <summary>
    /// Concrete XNA 4.0 implementation of <see cref="IEffectParameter"/>.
    /// </summary>
    public sealed class XNAEffectParameter : IEffectParameter {
        private XFG.EffectParameter _param;
        private XNAEffectParameterCollection _elements;
        private XNAEffectParameterCollection _structMembers;
        private XNAEffectAnnotationCollection _annotations;
        private EffectParameterClass _paramClass;
        private EffectParameterType _paramType;
        private Texture _cachedTexture;

        /// <summary>
        /// Gets the name of the parameter.
        /// </summary>
        public string Name {
            get {
                return _param.Name;
            }
        }

        /// <summary>
        /// Gets the semantic of the parameter, if it exists.
        /// </summary>
        public string Semantic {
            get {
                return _param.Semantic;
            }
        }

        /// <summary>
        /// Gets the array elements of the parameter, if it is an array.
        /// </summary>
        public IEffectParameterCollection Elements {
            get {
                return _elements;
            }
        }

        /// <summary>
        /// Gets the structure members of the parameter, if it is a struct.
        /// </summary>
        public IEffectParameterCollection StructureMembers {
            get {
                return _structMembers;
            }
        }

        /// <summary>
        /// Gets the collection of annotations that decorate the parameter.
        /// </summary>
        public IEffectAnnotationCollection Annotations {
            get {
                return _annotations;
            }
        }

        /// <summary>
        /// Gets the enumeration that specifies the parameter's classification.
        /// </summary>
        public EffectParameterClass ParameterClass {
            get {
                return _paramClass;
            }
        }

        /// <summary>
        /// Gets the enumeration that specifies the parameter's type.
        /// </summary>
        public EffectParameterType ParameterType {
            get {
                return _paramType;
            }
        }

        /// <summary>
        /// Gets the number of columns in the class of this parameter.
        /// </summary>
        public int ColumnCount {
            get {
                return _param.ColumnCount;
            }
        }

        /// <summary>
        /// Gets the number of rows in the class of this parameter.
        /// </summary>
        public int RowCount {
            get {
                return _param.RowCount;
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="XNAEffectParameter"/>.
        /// </summary>
        /// <param name="param">The underlying XNA effect parameter.</param>
        internal XNAEffectParameter(XFG.EffectParameter param) {
            _param = param;
            SetClassAndType(param.ParameterClass, param.ParameterType);
            _elements = new XNAEffectParameterCollection(param.Elements);
            _structMembers = new XNAEffectParameterCollection(param.StructureMembers);
            _annotations =  new XNAEffectAnnotationCollection(param.Annotations);
        }

        //Gets the class and type from the underlying parameter
        private void SetClassAndType(XFG.EffectParameterClass varClass, XFG.EffectParameterType varType) {
            switch(varClass) {
                case XFG.EffectParameterClass.Scalar:
                    _paramClass = EffectParameterClass.Scalar;
                    break;
                case XFG.EffectParameterClass.Vector:
                    _paramClass = EffectParameterClass.Vector;
                    break;
                case XFG.EffectParameterClass.Matrix:
                    _paramClass = EffectParameterClass.Matrix;
                    break;
                case XFG.EffectParameterClass.Object:
                    _paramClass = EffectParameterClass.Object;
                    break;
                case XFG.EffectParameterClass.Struct:
                    _paramClass = EffectParameterClass.Struct;
                    break;
            }
            switch(varType) {
                case XFG.EffectParameterType.Bool:
                    _paramType = EffectParameterType.Bool;
                    break;
                case XFG.EffectParameterType.Single:
                    _paramType = EffectParameterType.Single;
                    break;
                case XFG.EffectParameterType.Int32:
                    _paramType = EffectParameterType.Int32;
                    break;
                case XFG.EffectParameterType.String:
                    _paramType = EffectParameterType.String;
                    break;
                case XFG.EffectParameterType.Texture:
                    _paramType = EffectParameterType.Texture;
                    break;
                case XFG.EffectParameterType.Texture1D:
                    _paramType = EffectParameterType.Texture1D;
                    break;
                case XFG.EffectParameterType.Texture2D:
                    _paramType = EffectParameterType.Texture2D;
                    break;
                case XFG.EffectParameterType.Texture3D:
                    _paramType = EffectParameterType.Texture3D;
                    break;
                case XFG.EffectParameterType.TextureCube:
                    _paramType = EffectParameterType.TextureCube;
                    break;
                case XFG.EffectParameterType.Void:
                    _paramType = EffectParameterType.Void;
                    break;
            }
        }

        //Gets if an array or not
        private bool IsArray() {
            if(_elements.Count > 0) {
                return true;
            } else {
                return false;
            }
        }

        //Check if the variable is a Vector2, Vector3, or Vector4
        private bool IsVector(int count) {
            if(count < 2 || count > 4) {
                return false;
            }

            return _param.RowCount == 1 && _param.ColumnCount == count;
        }

        /// <summary>
        /// Gets the value of the parameter as a bool.
        /// </summary>
        /// <returns>
        /// Bool value
        /// </returns>
        public bool GetValueBoolean() {
            return _param.GetValueBoolean();
        }

        /// <summary>
        /// Gets the value of the parameter as an array of bools.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The bool array
        /// </returns>
        public bool[] GetValueBooleanArray(int count) {
            return _param.GetValueBooleanArray(count);
        }

        /// <summary>
        /// Gets the value of the parameter as a single.
        /// </summary>
        /// <returns>
        /// The single value
        /// </returns>
        public float GetValueSingle() {
            return _param.GetValueSingle();
        }

        /// <summary>
        /// Gets the value of the parameter as an array of singles.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The single array
        /// </returns>
        public float[] GetValueSingleArray(int count) {
            return _param.GetValueSingleArray(count);
        }

        /// <summary>
        /// Gets the value of the parameter as an int.
        /// </summary>
        /// <returns>
        /// The int value
        /// </returns>
        public int GetValueInt() {
            return _param.GetValueInt32();
        }

        /// <summary>
        /// Gets the value of the parameter as an array of ints.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The int array
        /// </returns>
        public int[] GetValueIntArray(int count) {
            return _param.GetValueInt32Array(count);
        }


        /// <summary>
        /// Gets the value of the parameter as a matrix.
        /// </summary>
        /// <returns>
        /// The matrix value
        /// </returns>
        public Matrix GetValueMatrix() {
            XNA.Matrix xm = _param.GetValueMatrix();
            Matrix m;
            XNAHelper.ConvertMatrix(ref xm, out m);
            return m;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of matrices.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The matrix array
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a matrix array.</exception>
        public Matrix[] GetValueMatrixArray(int count) {
            if(_paramClass != EffectParameterClass.Matrix || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Matrix[].");
            }

            if(count <= 0 || count > _elements.Count) {
                throw new ArgumentOutOfRangeException("count", "Element count is out of range.");
            }

            Matrix[] array = new Matrix[count];

            for(int i = 0; i < array.Length; i++) {
                array[i] = _elements[i].GetValueMatrix();
            }

            return array;
        }

        /// <summary>
        /// Gets the value of the parameter as a matrix transpose.
        /// </summary>
        /// <returns>
        /// The matrix transpose value
        /// </returns>
        public Matrix GetValueMatrixTranspose() {
            XNA.Matrix xm = _param.GetValueMatrixTranspose();
            Matrix m;
            XNAHelper.ConvertMatrix(ref xm, out m);
            return m;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of matrix transposes.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The matrix transpose array
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a matrix array.</exception>
        public Matrix[] GetValueMatrixTransposeArray(int count) {
            if(_paramClass != EffectParameterClass.Matrix || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Matrix[].");
            }

            if(count <= 0 || count > _elements.Count) {
                throw new ArgumentOutOfRangeException("count", "Element count is out of range.");
            }

            Matrix[] array = new Matrix[count];

            for(int i = 0; i < array.Length; i++) {
                array[i] = _elements[i].GetValueMatrixTranspose();
            }

            return array;
        }

        /// <summary>
        /// Gets the value of the parameter as a quaternion.
        /// </summary>
        /// <returns>
        /// The quaternion value
        /// </returns>
        public Quaternion GetValueQuaternion() {
            XNA.Quaternion xq = _param.GetValueQuaternion();
            Quaternion q;
            XNAHelper.ConvertQuaternion(ref xq, out q);
            return q;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of quaternions.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The quaternion array
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a quaternion array.</exception>
        public Quaternion[] GetValueQuaternionArray(int count) {
            if(_paramClass != EffectParameterClass.Vector || !IsVector(4) ||!IsArray()) {
                throw new InvalidCastException("Parameter is not a Quaternion[].");
            }

            if(count <= 0 || count > _elements.Count) {
                throw new ArgumentOutOfRangeException("count", "Element count is out of range.");
            }

            Quaternion[] array = new Quaternion[count];

            for(int i = 0; i < array.Length; i++) {
                array[i] = _elements[i].GetValueQuaternion();
            }

            return array;
        }

        /// <summary>
        /// Gets the value of the parameter as a vector2.
        /// </summary>
        /// <returns>
        /// The vector2 value
        /// </returns>
        public Vector2 GetValueVector2() {
            XNA.Vector2 xv = _param.GetValueVector2();
            Vector2 v;
            XNAHelper.ConvertVector2(ref xv, out v);
            return v;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of vector2's.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The vector2 array
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a vector2 array.</exception>
        public Vector2[] GetValueVector2Array(int count) {
            if(_paramClass != EffectParameterClass.Vector || !IsVector(2) || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Vector2[].");
            }

            if(count <= 0 || count > _elements.Count) {
                throw new ArgumentOutOfRangeException("count", "Element count is out of range.");
            }

            Vector2[] array = new Vector2[count];

            for(int i = 0; i < array.Length; i++) {
                array[i] = _elements[i].GetValueVector2();
            }

            return array;
        }

        /// <summary>
        /// Gets the value of the parameter as a vector3.
        /// </summary>
        /// <returns>
        /// The vector3 value
        /// </returns>
        public Vector3 GetValueVector3() {
            XNA.Vector3 xv = _param.GetValueVector3();
            Vector3 v;
            XNAHelper.ConvertVector3(ref xv, out v);
            return v;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of vector3's.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The vector3 array
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a vector3 array.</exception>
        public Vector3[] GetValueVector3Array(int count) {
            if(_paramClass != EffectParameterClass.Vector || !IsVector(3) || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Vector3[].");
            }

            if(count <= 0 || count > _elements.Count) {
                throw new ArgumentOutOfRangeException("count", "Element count is out of range.");
            }

            Vector3[] array = new Vector3[count];

            for(int i = 0; i < array.Length; i++) {
                array[i] = _elements[i].GetValueVector3();
            }

            return array;
        }

        /// <summary>
        /// Gets the value of the parameter as a vector4.
        /// </summary>
        /// <returns>
        /// The vector4 value
        /// </returns>
        public Vector4 GetValueVector4() {
            XNA.Vector4 xv = _param.GetValueVector4();
            Vector4 v;
            XNAHelper.ConvertVector4(ref xv, out v);
            return v;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of vector4's.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// The vector4 array
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a vector4 array.</exception>
        public Vector4[] GetValueVector4Array(int count) {
            if(_paramClass != EffectParameterClass.Vector || !IsVector(4) || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Vector4[].");
            }

            if(count <= 0 || count > _elements.Count) {
                throw new ArgumentOutOfRangeException("count", "Element count is out of range.");
            }

            Vector4[] array = new Vector4[count];

            for(int i = 0; i < array.Length; i++) {
                array[i] = _elements[i].GetValueVector4();
            }

            return array;
        }

        /// <summary>
        /// Gets the value of the parameter as a String.
        /// </summary>
        /// <returns>
        /// The string value
        /// </returns>
        public string GetValueString() {
            return _param.GetValueString();
        }

        /// <summary>
        /// Gets the value of the parameter as a Texture1D.
        /// </summary>
        /// <returns>
        /// The texture value
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Texture1D.</exception>
        public Texture1D GetValueTexture1D() {
            if(_paramClass != EffectParameterClass.Object || _paramType != EffectParameterType.Texture1D) {
                throw new InvalidCastException("Parameter is not a Texture1D");
            }

            return _cachedTexture as Texture1D;
        }

        /// <summary>
        /// Gets the value of the parameter as a Texture2D.
        /// </summary>
        /// <returns>
        /// The texture value
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Texture2D.</exception>
        public Texture2D GetValueTexture2D() {
            if(_paramClass != EffectParameterClass.Object || _paramType != EffectParameterType.Texture2D) {
                throw new InvalidCastException("Parameter is not a Texture2D");
            }

            return _cachedTexture as Texture2D;
        }

        /// <summary>
        /// Gets the value of the parameter as a Texture3D.
        /// </summary>
        /// <returns>
        /// The texture value
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Texture3D.</exception>
        public Texture3D GetValueTexture3D() {
            if(_paramClass != EffectParameterClass.Object || _paramType != EffectParameterType.Texture3D) {
                throw new InvalidCastException("Parameter is not a Texture3D");
            }

            return _cachedTexture as Texture3D;
        }

        /// <summary>
        /// Gets the value of the parameter as a TextureCube.
        /// </summary>
        /// <returns>
        /// The texture value
        /// </returns>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a TextureCube.</exception>
        public TextureCube GetValueTextureCube() {
            if(_paramClass != EffectParameterClass.Object || _paramType != EffectParameterType.TextureCube) {
                throw new InvalidCastException("Parameter is not a TextureCube");
            }

            return _cachedTexture as TextureCube;
        }

        /// <summary>
        /// Gets the value of the parameter as an array of textures.
        /// </summary>
        /// <param name="count">Number of elements to get</param>
        /// <returns>
        /// Texture array
        /// </returns>
        public Texture[] GetValueTextureArray(int count) {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Bool value</param>
        public void SetValue(bool value) {
            _param.SetValue(value);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of bools</param>
        public void SetValue(bool[] values) {
            _param.SetValue(values);
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Single value</param>
        public void SetValue(float value) {
            _param.SetValue(value);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of singles</param>
        public void SetValue(float[] values) {
            _param.SetValue(values);
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Int value</param>
        public void SetValue(int value) {
            _param.SetValue(value);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of ints</param>
        public void SetValue(int[] values) {
            _param.SetValue(values);
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Matrix value</param>
        public void SetValue(Matrix value) {
            XNA.Matrix m;
            XNAHelper.ConvertMatrix(ref value, out m);

            _param.SetValue(m);
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Matrix transpose value</param>
        public void SetValueTranspose(Matrix value) {
            XNA.Matrix m;
            XNAHelper.ConvertMatrix(ref value, out m);
            _param.SetValueTranspose(m);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of transposed matrices</param>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Matrix[].</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the array is too large to be set</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if the array is null or of empty length.</exception>
        public void SetValueTranspose(Matrix[] values) {
            if(_paramClass != EffectParameterClass.Matrix || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Matrix[]");
            }

            if(values == null || values.Length == 0) {
                throw new ArgumentNullException("Array cannot be null");
            }

            if(values.Length <= _elements.Count) {
                for(int i = 0; i < values.Length; i++) {
                    _elements[i].SetValueTranspose(values[i]);
                }
            } else {
                throw new ArgumentOutOfRangeException("Matrix array is too large to be set to shader parameter.");
            }
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of matrices</param>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Matrix[].</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the array is too large to be set</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if the array is null or of empty length.</exception>
        public void SetValue(Matrix[] values) {
            if(_paramClass != EffectParameterClass.Matrix || !IsArray()) {
                throw new InvalidCastException("Parameter is not a Matrix[]");
            }

            if(values == null || values.Length == 0) {
                throw new ArgumentNullException("Array cannot be null");
            }

            if(values.Length <= _elements.Count) {
                for(int i = 0; i < values.Length; i++) {
                    _elements[i].SetValue(values[i]);
                }
            } else {
                throw new ArgumentOutOfRangeException("Matrix array is too large to be set to shader parameter.");
            }
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Vector2 value</param>
        public void SetValue(Vector2 value) {
            XNA.Vector2 v;
            v.X = value.X;
            v.Y = value.Y;
            _param.SetValue(v);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of Vector2</param>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Vector2[].</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the array is too large to be set</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if the array is null or of empty length.</exception>
        public void SetValue(Vector2[] values) {
            if(_paramClass != EffectParameterClass.Vector || !IsArray() || !IsVector(2)) {
                throw new InvalidCastException("Parameter is not a Vector2[].");
            }

            if(values == null || values.Length == 0) {
                throw new ArgumentNullException("Array cannot be null");
            }

            if(values.Length <= _elements.Count) {
                for(int i = 0; i < values.Length; i++) {
                    _elements[i].SetValue(values[i]);
                }
            } else {
                throw new ArgumentOutOfRangeException("Vector2 array is too large to be set to shader parameter.");
            }
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Vector3 value</param>
        public void SetValue(Vector3 value) {
            XNA.Vector3 v;
            v.X = value.X;
            v.Y = value.Y;
            v.Z = value.Z;
            _param.SetValue(v);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of Vector3</param>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Vector3[].</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the array is too large to be set</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if the array is null or of empty length.</exception>
        public void SetValue(Vector3[] values) {
            if(_paramClass != EffectParameterClass.Vector || !IsArray() || !IsVector(3)) {
                throw new InvalidCastException("Parameter is not a Vector3[]");
            }

            if(values == null || values.Length == 0) {
                throw new ArgumentNullException("Array cannot be null");
            }

            if(values.Length <= _elements.Count) {
                for(int i = 0; i < values.Length; i++) {
                    _elements[i].SetValue(values[i]);
                }
            } else {
                throw new ArgumentOutOfRangeException("Vector3 array is too large to be set to shader parameter.");
            }
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Vector4 value</param>
        public void SetValue(Vector4 value) {
            XNA.Vector4 v;
            v.X = value.X;
            v.Y = value.Y;
            v.Z = value.Z;
            v.W = value.W;
            _param.SetValue(v);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of Vector4</param>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Matrix[].</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the array is too large to be set</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if the array is null or of empty length.</exception>
        public void SetValue(Vector4[] values) {
            if(_paramClass != EffectParameterClass.Vector || !IsArray() || !IsVector(4)) {
                throw new InvalidCastException("Parameter is not a Vector4[].");
            }

            if(values == null || values.Length == 0) {
                throw new ArgumentNullException("Array cannot be null");
            }

            if(values.Length <= _elements.Count) {
                for(int i = 0; i < values.Length; i++) {
                    _elements[i].SetValue(values[i]);
                }
            } else {
                throw new ArgumentOutOfRangeException("Vector4 array is too large to be set to shader parameter.");
            }
        }

        /// <summary>
        /// Sets the value of the parameter.
        /// </summary>
        /// <param name="value">Quaternion value</param>
        public void SetValue(Quaternion value) {
            XNA.Quaternion q;
            q.X = value.X;
            q.Y = value.Y;
            q.Z = value.Z;
            q.W = value.W;
            _param.SetValue(q);
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of quaternions</param>
        /// <exception cref="System.InvalidCastException">Thrown if the parameter is not a Quaternion[].</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the array is too large to be set</exception>
        /// <exception cref="System.ArgumentNullException">Thrown if the array is null or of empty length.</exception>
        public void SetValue(Quaternion[] values) {
            if(_paramClass != EffectParameterClass.Vector || !IsArray() || !IsVector(4)) {
                throw new InvalidCastException("Error setting quaternion[] to shader parameter.");
            }

            if(values == null || values.Length == 0) {
                throw new ArgumentNullException("Array cannot be null");
            }

            if(values.Length <= _elements.Count) {
                for(int i = 0; i < values.Length; i++) {
                    _elements[i].SetValue(values[i]);
                }
            } else {
                throw new ArgumentOutOfRangeException("Quaternion array is too large to be set to shader parameter.");
            }
        }

        /// <summary>
        /// Sets the value.
        /// </summary>
        /// <param name="value">The texture.</param>
        public void SetValue(Texture value) {
            if(_paramClass != EffectParameterClass.Object) {
                throw new InvalidCastException("Parameter is not a texture.");
            }

            if(value == null) {
                throw new ArgumentNullException("Texture cannot be null");
            }

            switch(_paramType) {
                case EffectParameterType.Texture1D:
                    Texture1D tex1D = value as Texture1D;
                    if(tex1D == null) {
                        throw new InvalidCastException("Expecting Texture1D");
                    }
                    _param.SetValue(((XNATexture1DImplementation) tex1D.Implementation).XNATexture1D);
                    _cachedTexture = value;
                    break;
                case EffectParameterType.Texture2D:
                    Texture2D tex2D = value as Texture2D;
                    if(tex2D == null) {
                        throw new InvalidCastException("Expecting Texture2D");
                    }
                    _param.SetValue(((XNATexture2DImplementation) tex2D.Implementation).XNATexture2D);
                    _cachedTexture = value;
                    break;
                case EffectParameterType.Texture3D:
                    Texture3D tex3D = value as Texture3D;
                    if(tex3D == null) {
                        throw new InvalidCastException("Expecting Texture3D");
                    }
                    _param.SetValue(((XNATexture3DImplementation) tex3D.Implementation).XNATexture3D);
                    _cachedTexture = value;
                    break;
                case EffectParameterType.TextureCube:
                    TextureCube texCube = value as TextureCube;
                    if(texCube == null) {
                        throw new InvalidCastException("Expecting Texture3D");
                    }
                    _param.SetValue(((XNATextureCubeImplementation) texCube.Implementation).XNATextureCube);
                    _cachedTexture = value;
                    break;
            }
        }

        /// <summary>
        /// Sets the value of the parameter's elements.
        /// </summary>
        /// <param name="values">Array of textures</param>
        public void SetValue(Texture[] values) {
            throw new NotImplementedException();
        }

        #region IEffectParameter Members


        public Color GetValueColor() {
            throw new NotImplementedException();
        }

        public Color[] GetValueColorArray(int count) {
            throw new NotImplementedException();
        }

        public void SetValue(Color value) {
            throw new NotImplementedException();
        }

        public void SetValue(Color[] values) {
            throw new NotImplementedException();
        }

        #endregion
    }
}
